package RayTracing;


//using http://www.codeproject.com/Articles/405128/Matrix-operations-in-Java
public class Matrix {

    private int nrows;
    private int ncols;
    private double[][] data;

    public int getNrows() {
		return nrows;
	}

	public void setNrows(int nrows) {
		this.nrows = nrows;
	}

	public int getNcols() {
		return ncols;
	}

	public void setNcols(int ncols) {
		this.ncols = ncols;
	}

	public double[][] getData() {
		return data;
	}

	public void setData(double[][] data) {
		this.data = data;
	}

	public Matrix(double[][] dat) {
        this.data = dat;
        this.nrows = dat.length;
        this.ncols = dat[0].length;
    }

    public Matrix(int nrow, int ncol) {
        this.nrows = nrow;
        this.ncols = ncol;
        data = new double[nrow][ncol];
    }
    
    public static Matrix transpose(Matrix matrix) {
        Matrix transposedMatrix = new Matrix(matrix.getNcols(), matrix.getNrows());
        for (int i=0;i<matrix.getNrows();i++) {
            for (int j=0;j<matrix.getNcols();j++) {
                transposedMatrix.setValueAt(j, i, matrix.getValueAt(i, j));
            }
        }
        return transposedMatrix;
    } 
    
    private void setValueAt(int j, int i, double valueAt) {
    	data[i][j] = valueAt;
		
	}

	public static double determinant(Matrix matrix)  {

        if (matrix.size() == 1) {
    	return matrix.getValueAt(0, 0);
        }
        if (matrix.size()==2) {
            return (matrix.getValueAt(0, 0) * matrix.getValueAt(1, 1)) - ( matrix.getValueAt(0, 1) * matrix.getValueAt(1, 0));
        }
        double sum = 0.0;
        for (int i=0; i<matrix.getNcols(); i++) {
            sum += changeSign(i) * matrix.getValueAt(0, i) * determinant(createSubMatrix(matrix, 0, i));
        }
        return sum;
    } 
    
    private int size() {
		// TODO Auto-generated method stub
		return nrows ;
	}

	private static double changeSign(int i) {
		// TODO Auto-generated method stub
		if ((i % 2) == 0)
			return -1;
		else
			return 1;
	}

	private boolean isSquare() {
		// TODO Auto-generated method stub
		return (nrows == ncols);
	}

	private double getValueAt(int i, int j) {
    	return data[i][j];

	}

	public static Matrix createSubMatrix(Matrix matrix, int excluding_row, int excluding_col) {
        Matrix mat = new Matrix(matrix.getNrows()-1, matrix.getNcols()-1);
        int r = -1;
        for (int i=0;i<matrix.getNrows();i++) {
            if (i==excluding_row)
                continue;
                r++;
                int c = -1;
            for (int j=0;j<matrix.getNcols();j++) {
                if (j==excluding_col)
                    continue;
                mat.setValueAt(r, ++c, matrix.getValueAt(i, j));
            }
        }
        return mat;
    } 
    
    public static Matrix cofactor(Matrix matrix) {
        Matrix mat = new Matrix(matrix.getNrows(), matrix.getNcols());
        for (int i=0;i<matrix.getNrows();i++) {
            for (int j=0; j<matrix.getNcols();j++) {
                mat.setValueAt(i, j, changeSign(i) * changeSign(j) * determinant(createSubMatrix(matrix, i, j)));
            }
        }
        
        return mat;
    }
    
    public static Matrix inverse(Matrix matrix) {
        return (transpose(cofactor(matrix)).multiplyByConstant(1.0/determinant(matrix)));
    }

	private Matrix multiplyByConstant(double d) {
		// TODO Auto-generated method stub
		Matrix invMat = new Matrix(nrows, ncols);
        for (int i=0;i < nrows ; i++) {
            for (int j=0; j< ncols; j++) {
            	invMat.data[i][j] = data[i][j] * d;
            }
      }
		return invMat;

	}

}
